# 组合包装推荐逻辑实现

import pandas as pd
import logging
from src.config.constants import CLASSIFICATION_CATEGORIES, PACKAGE_TYPE, FIELD_NAMES


def _process_combination_references(data):
    """处理组合包装参考项，遵循大纲中的组合包装处理逻辑

    Args:
        data: 待处理的数据

    Returns:
        处理后的数据
    """
    # 初始化logger
    logger = logging.getLogger("combination_reference_processing")
    
    # 数据有效性验证
    if data is None or data.empty:
        logger.warning("待处理数据为空，无法处理组合包装参考项")
        return data
    
    # 创建数据副本以避免修改原始数据
    result_data = data.copy()
    
    # 检查必需的字段
    required_fields = ["备注", "匹配历史记录"]
    for field in required_fields:
        if field not in result_data.columns:
            logger.warning(f"数据中不包含必需字段 '{field}'，尝试使用备用字段或创建空字段")
            if field == "备注" and FIELD_NAMES.get("MATCH_STATUS") in result_data.columns:
                result_data["备注"] = result_data[FIELD_NAMES["MATCH_STATUS"]]
            elif field == "匹配历史记录":
                result_data["匹配历史记录"] = None
            else:
                result_data[field] = None
    
    # 1. 筛选需要处理的组合包装参考项
    combination_mask = result_data["备注"] == CLASSIFICATION_CATEGORIES["REFERENCE_COMBINATION"]
    combination_count = combination_mask.sum()
    
    if combination_count == 0:
        logger.debug("没有找到组合包装参考项，无需处理")
        return result_data
    
    logger.info(f"找到 {combination_count} 个组合包装参考项，开始处理")
    
    # 2. 获取所有组合包装参考项的索引
    combination_indices = result_data[combination_mask].index
    
    # 处理计数
    processed_count = 0
    success_count = 0
    failed_count = 0
    
    # 3. 为每个组合包装参考项查找历史组合包装方案
    for idx in combination_indices:
        try:
            processed_count += 1
            
            # 获取匹配的历史记录
            historical_records = result_data.loc[idx, "匹配历史记录"]
            
            if historical_records is None or historical_records.empty:
                logger.warning(f"记录 {idx} 的匹配历史记录为空，无法推荐组合包装方案")
                failed_count += 1
                continue
            
            # 筛选历史组合包装记录
            package_type_field = FIELD_NAMES.get("PACKAGE_TYPE", "包装类型")
            if package_type_field not in historical_records.columns:
                logger.warning(f"历史记录中不包含包装类型字段 '{package_type_field}'，无法筛选组合包装记录")
                failed_count += 1
                continue
            
            combo_records = historical_records[historical_records[package_type_field] == PACKAGE_TYPE["COMBINATION"]]
            
            if combo_records.empty:
                logger.warning(f"记录 {idx} 的历史记录中没有找到组合包装方案")
                failed_count += 1
                continue
            
            # 选择最佳组合包装方案
            try:
                # 优先按匹配得分排序
                if "匹配得分" in combo_records.columns:
                    best_combo = combo_records.sort_values(by="匹配得分", ascending=False).iloc[0]
                # 如果没有匹配得分，按最近日期排序
                elif FIELD_NAMES.get("DATE") in combo_records.columns:
                    best_combo = combo_records.sort_values(by=FIELD_NAMES["DATE"], ascending=False).iloc[0]
                # 否则选择第一条记录
                else:
                    best_combo = combo_records.iloc[0]
                    logger.info(f"记录 {idx} 的组合包装记录没有匹配得分和日期字段，选择第一条记录")
                
                # 复制包装信息
                from .classification import _copy_package_info
                _copy_package_info(result_data, best_combo, idx)
                
                # 添加组合包装标记
                result_data.loc[idx, "组合包装标记"] = "是"
                success_count += 1
                logger.debug(f"成功为记录 {idx} 推荐组合包装方案")
                
            except Exception as e:
                logger.error(f"选择或应用最佳组合包装方案时发生错误: {str(e)}")
                failed_count += 1
        except Exception as e:
            logger.error(f"处理记录 {idx} 时发生错误: {str(e)}")
            failed_count += 1
    
    # 记录处理结果
    logger.info(f"组合包装参考项处理完成，共处理 {processed_count} 个记录，成功 {success_count} 个，失败 {failed_count} 个")
    
    return result_data

    return data


def _process_mixed_references(data):
    """处理混合包装参考项

    Args:
        data: 待处理的数据

    Returns:
        处理后的数据
    """
    # 初始化logger
    logger = logging.getLogger("mixed_reference_processing")
    
    # 数据有效性验证
    if data is None or data.empty:
        logger.warning("待处理数据为空，无法处理混合包装参考项")
        return data
    
    # 创建数据副本以避免修改原始数据
    result_data = data.copy()
    
    # 检查必需的字段
    required_fields = ["备注", "匹配历史记录"]
    for field in required_fields:
        if field not in result_data.columns:
            logger.warning(f"数据中不包含必需字段 '{field}'，尝试使用备用字段或创建空字段")
            if field == "备注" and FIELD_NAMES.get("MATCH_STATUS") in result_data.columns:
                result_data["备注"] = result_data[FIELD_NAMES["MATCH_STATUS"]]
            elif field == "匹配历史记录":
                result_data["匹配历史记录"] = None
            else:
                result_data[field] = None
    
    # 1. 筛选需要处理的混合包装参考项
    mixed_mask = result_data["备注"] == CLASSIFICATION_CATEGORIES["REFERENCE_MIXED"]
    mixed_count = mixed_mask.sum()
    
    if mixed_count == 0:
        logger.debug("没有找到混合包装参考项，无需处理")
        return result_data
    
    logger.info(f"找到 {mixed_count} 个混合包装参考项，开始处理")
    
    # 2. 获取所有混合包装参考项的索引
    mixed_indices = result_data[mixed_mask].index
    
    # 处理计数
    processed_count = 0
    success_count = 0
    failed_count = 0
    
    # 3. 为每个混合包装参考项查找合适的包装方案
    for idx in mixed_indices:
        try:
            processed_count += 1
            
            # 获取匹配的历史记录
            historical_records = result_data.loc[idx, "匹配历史记录"]
            
            if historical_records is None or historical_records.empty:
                logger.warning(f"记录 {idx} 的匹配历史记录为空，无法推荐包装方案")
                failed_count += 1
                continue
            
            # 获取包装类型字段名
            package_type_field = FIELD_NAMES.get("PACKAGE_TYPE", "包装类型")
            
            # 优先选择混合包装记录
            if package_type_field in historical_records.columns:
                mixed_records = historical_records[historical_records[package_type_field] == PACKAGE_TYPE["MIXED"]]
                
                if not mixed_records.empty:
                    try:
                        # 按匹配得分排序选择最佳方案
                        if "匹配得分" in mixed_records.columns:
                            best_mixed = mixed_records.sort_values(by="匹配得分", ascending=False).iloc[0]
                        elif FIELD_NAMES.get("DATE") in mixed_records.columns:
                            best_mixed = mixed_records.sort_values(by=FIELD_NAMES["DATE"], ascending=False).iloc[0]
                        else:
                            best_mixed = mixed_records.iloc[0]
                            logger.info(f"记录 {idx} 的混合包装记录没有匹配得分和日期字段，选择第一条记录")
                        
                        # 复制包装信息
                        from .classification import _copy_package_info
                        _copy_package_info(result_data, best_mixed, idx)
                        
                        # 添加混合包装标记
                        result_data.loc[idx, "混合包装标记"] = "是"
                        success_count += 1
                        logger.debug(f"成功为记录 {idx} 推荐混合包装方案")
                        continue  # 成功推荐后跳过后续逻辑
                    except Exception as e:
                        logger.error(f"选择或应用最佳混合包装方案时发生错误: {str(e)}")
                        # 继续尝试其他方案
            else:
                logger.warning(f"历史记录中不包含包装类型字段 '{package_type_field}'，无法筛选混合包装记录")
            
            # 如果没有混合包装记录或处理失败，使用最佳匹配的任何类型
            try:
                # 按匹配得分排序选择最佳方案
                if "匹配得分" in historical_records.columns:
                    best_record = historical_records.sort_values(by="匹配得分", ascending=False).iloc[0]
                elif FIELD_NAMES.get("DATE") in historical_records.columns:
                    best_record = historical_records.sort_values(by=FIELD_NAMES["DATE"], ascending=False).iloc[0]
                else:
                    best_record = historical_records.iloc[0]
                    logger.info(f"记录 {idx} 的历史记录没有匹配得分和日期字段，选择第一条记录")
                
                # 复制包装信息
                from .classification import _copy_package_info
                _copy_package_info(result_data, best_record, idx)
                
                # 添加混合包装标记
                result_data.loc[idx, "混合包装标记"] = "是"
                success_count += 1
                logger.debug(f"成功为记录 {idx} 推荐最佳匹配包装方案")
                
            except Exception as e:
                logger.error(f"选择或应用最佳匹配包装方案时发生错误: {str(e)}")
                failed_count += 1
        except Exception as e:
            logger.error(f"处理记录 {idx} 时发生错误: {str(e)}")
            failed_count += 1
    
    # 记录处理结果
    logger.info(f"混合包装参考项处理完成，共处理 {processed_count} 个记录，成功 {success_count} 个，失败 {failed_count} 个")
    
    return result_data

    return data


def recommend_combination_packages(data):
    """推荐组合包装方案

    实现大纲4.8.4节组合包装处理逻辑，根据以下策略进行组合包装推荐：
    1. 对于同一客户、同一预完工日期的订单，尝试将多个小批量产品组合包装
    2. 考虑产品兼容性、尺寸匹配度、客户偏好等因素
    3. 优化组合包装方案，计算成本效益比

    Args:
        data: 待处理的数据

    Returns:
        添加了推荐组合包装的数据
    """
    # 初始化logger
    logger = logging.getLogger("combination_recommendation")
    
    # 数据有效性验证
    if data is None or data.empty:
        logger.warning("待处理数据为空，无法推荐组合包装方案")
        return data
    
    # 创建数据副本以避免修改原始数据
    result_data = data.copy()
    
    # 检查必需的字段
    required_fields = ["备注", "客户简称", "预完工日期", FIELD_NAMES.get("SPECIFICATION_MODEL", "规格型号"), FIELD_NAMES.get("QUANTITY", "数量")]
    for field in required_fields:
        if field not in result_data.columns:
            logger.warning(f"数据中不包含必需字段 '{field}'，尝试使用备用字段或创建空字段")
            if field == "备注" and FIELD_NAMES.get("MATCH_STATUS") in result_data.columns:
                result_data["备注"] = result_data[FIELD_NAMES["MATCH_STATUS"]]
            else:
                result_data[field] = None
    
    # 1. 筛选适合组合包装的记录
    # 包括：未指定包装类型的记录、参考项（组合和混合）
    combo_eligibility_mask = (
        (result_data["备注"] == CLASSIFICATION_CATEGORIES["REFERENCE_COMBINATION"])
        | (result_data["备注"] == CLASSIFICATION_CATEGORIES["REFERENCE_MIXED"])
        | (result_data["备注"] == CLASSIFICATION_CATEGORIES["REFERENCE_INDEPENDENT"])
    )
    
    eligible_count = combo_eligibility_mask.sum()
    
    if eligible_count == 0:
        logger.debug("没有找到适合组合包装的记录，无需推荐组合包装方案")
        return result_data
    
    logger.info(f"找到 {eligible_count} 个适合组合包装的记录，开始推荐组合包装方案")
    
    # 获取客户简称和预完工日期字段名
    customer_field = "客户简称"
    date_field = "预完工日期"
    
    # 2. 按客户简称和预完工日期分组
    if customer_field not in result_data.columns or date_field not in result_data.columns:
        logger.warning("数据中缺少客户简称或预完工日期字段，无法按组推荐组合包装")
        # 尝试使用备用字段
        if FIELD_NAMES.get("CUSTOMER") in result_data.columns:
            customer_field = FIELD_NAMES["CUSTOMER"]
        if FIELD_NAMES.get("DATE") in result_data.columns:
            date_field = FIELD_NAMES["DATE"]
        else:
            logger.warning("无法找到合适的分组字段，返回原始数据")
            return result_data
    
    # 获取符合条件的记录
    eligible_data = result_data[combo_eligibility_mask]
    
    # 3. 按客户和日期分组处理
    try:
        grouped = eligible_data.groupby([customer_field, date_field])
        
        # 初始化组合包装ID计数器
        combo_id_counter = 1
        
        # 处理每个分组
        for group_key, group_data in grouped:
            customer_name = group_key[0]
            delivery_date = group_key[1]
            group_size = len(group_data)
            
            logger.info(f"处理客户 '{customer_name}' 在 {delivery_date} 的订单组，包含 {group_size} 个记录")
            
            # 只有当组内记录数大于1时才考虑组合包装
            if group_size > 1:
                # 尝试生成组合包装方案
                try:
                    # 计算组内总数量和平均数量
                    quantity_field = FIELD_NAMES.get("QUANTITY", "数量")
                    if quantity_field in group_data.columns:
                        # 尝试将数量字段转换为数值类型
                        group_data[quantity_field] = pd.to_numeric(group_data[quantity_field], errors='coerce')
                        total_quantity = group_data[quantity_field].sum()
                        avg_quantity = group_data[quantity_field].mean()
                        
                        logger.debug(f"订单组总数量: {total_quantity}, 平均数量: {avg_quantity}")
                        
                        # 定义组合包装的阈值条件
                        # 这里使用简单的阈值判断，实际应用中需要更复杂的算法
                        small_batch_threshold = 50  # 小批量的阈值
                        
                        # 统计小批量记录数量
                        small_batch_count = (group_data[quantity_field] <= small_batch_threshold).sum()
                        
                        logger.debug(f"订单组中小批量记录数量: {small_batch_count}")
                        
                        # 如果小批量记录占比较高，推荐组合包装
                        if small_batch_count >= 2 and small_batch_count / group_size >= 0.5:
                            # 为这组记录分配组合包装ID
                            combo_id = f"COMBO_{combo_id_counter}"
                            combo_id_counter += 1
                            
                            # 标记组合包装
                            for idx in group_data.index:
                                result_data.loc[idx, "推荐组合包装"] = "是"
                                result_data.loc[idx, "组合包装ID"] = combo_id
                                result_data.loc[idx, "组合包装策略"] = f"同一客户同一日期的小批量产品组合包装，共{group_size}个产品"
                                result_data.loc[idx, "组合包装说明"] = f"将多个小批量产品合并为一组包装，以降低包装成本和提高物流效率"
                            
                            logger.info(f"为订单组推荐组合包装，组合包装ID: {combo_id}，包含 {group_size} 个记录")
                        else:
                            logger.debug("订单组不满足组合包装条件（小批量记录数量不足或占比过低）")
                    else:
                        logger.warning(f"组数据中不包含数量字段 '{quantity_field}'，无法计算组合包装条件")
                except Exception as e:
                    logger.error(f"为订单组生成组合包装方案时发生错误: {str(e)}")
            else:
                logger.debug("订单组记录数量少于2，不考虑组合包装")
    except Exception as e:
        logger.error(f"按客户和日期分组处理时发生错误: {str(e)}")
    
    # 4. 调用专门的组合包装参考项和混合包装参考项处理函数
    try:
        result_data = _process_combination_references(result_data)
        result_data = _process_mixed_references(result_data)
    except Exception as e:
        logger.error(f"处理组合包装和混合包装参考项时发生错误: {str(e)}")
    
    # 统计最终推荐结果
    if "推荐组合包装" in result_data.columns:
        recommended_count = (result_data["推荐组合包装"] == "是").sum()
        logger.info(f"组合包装推荐完成，共推荐 {recommended_count} 个记录使用组合包装")
    
    return result_data
